package com.choudou5.solr.utils;

import cn.hutool.core.collection.CollUtil;
import com.choudou5.solr.bean.common.FileBean;
import com.choudou5.solr.util.exception.BizException;
import com.choudou5.solr.util.ConfigUtil;
import com.choudou5.solr.util.SysSeqUtil;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * @Name：UpdateUtil 说明
 * @@author choudou5
 * @@date 2018-07-14
 */
public class UploadUtil {

    private static final Logger logger = LoggerFactory.getLogger(UploadUtil.class);

    private static final String UPLOAD_PATH = ConfigUtil.getStr("upload.dir");
    private static final long UPLOAD_MAX = ConfigUtil.getLong("upload.max");

    /**
     * 获得 上传文件
     * @param request
     * @return
     * @throws Exception
     */
    public static FileBean getUploadFile(HttpServletRequest request) throws BizException {
        List<FileBean> list = getUploadFileList(request);
        if(CollUtil.isNotEmpty(list))
            return list.get(0);
        return null;
    }

    /**
     * 获得 上传文件
     * @param request
     * @return
     * @throws Exception
     */
    public static List<FileBean> getUploadFileList(HttpServletRequest request) throws BizException{
        //上传时生成的临时文件保存目录
        File uploadDir = new File(UPLOAD_PATH);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }

        List<FileBean> result = new ArrayList<>();
        try{
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置工厂的缓冲区的大小，当上传的文件大小超过缓冲区的大小时，就会生成一个临时文件存放到指定的临时目录当中。
            factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB，如果不指定，那么缓冲区的大小默认是10KB
            factory.setRepository(uploadDir);
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //监听文件上传进度
            upload.setProgressListener(new ProgressListener(){
                public void update(long pBytesRead, long pContentLength, int arg2) {
                    logger.info("上传的文件大小为：" + pContentLength + ",当前已处理：" + pBytesRead);
                }
            });
            //解决上传文件名的中文乱码
            upload.setHeaderEncoding("UTF-8");
            //3、判断提交上来的数据是否是上传表单的数据
            if(!ServletFileUpload.isMultipartContent(request)){
                return null;
            }
            upload.setFileSizeMax(UPLOAD_MAX);
            //设置上传文件总量的最大值
            upload.setSizeMax(UPLOAD_MAX*10);
            //4、使用ServletFileUpload解析器解析上传数据，解析结果返回的是一个List<FileItem>集合，每一个FileItem对应一个Form表单的输入项
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem item : list){
                FileBean fileBean = new FileBean();
                //如果fileitem中封装的是普通输入项的数据
                if(!item.isFormField()){
                    //得到上传的文件名称，
                    String aliasName = item.getName();
                    aliasName = aliasName.substring(aliasName.lastIndexOf("\\")+1);
                    fileBean.setAliasName(aliasName);
                    //得到上传文件的扩展名
                    fileBean.setPrefix(aliasName.substring(aliasName.lastIndexOf(".") + 1));
                    //如果需要限制上传的文件类型，那么可以通过文件的扩展名来判断上传的文件类型是否合法
                    //获取item中的上传文件的输入流
                    InputStream in = item.getInputStream();
                    //得到文件保存的名称
                    String saveFilename = SysSeqUtil.generateCode();
                    //得到文件的保存目录
                    String realSavePath = makePath(saveFilename, UPLOAD_PATH);
                    String filePath = realSavePath + "\\" + saveFilename;
                    fileBean.setFilePath(filePath);
                    //创建一个文件输出流
                    FileOutputStream out = new FileOutputStream(filePath);
                    //创建一个缓冲区
                    byte buffer[] = new byte[1024];
                    //判断输入流中的数据是否已经读完的标识
                    int len = 0;
                    //循环将输入流读入到缓冲区当中，(len=in.read(buffer))>0就表示in里面还有数据
                    while((len=in.read(buffer))>0){
                        //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
                        out.write(buffer, 0, len);
                    }
                    //关闭输入流
                    in.close();
                    //关闭输出流
                    out.close();
                    //删除处理文件上传时生成的临时文件
                    //item.delete();
                }
            }
        }catch (FileUploadBase.FileSizeLimitExceededException e) {
            logger.error("个文件超出最大值", e);
            throw new BizException("个文件超出最大值");
        }catch (FileUploadBase.SizeLimitExceededException e) {
            logger.error("上传文件的总的大小超出限制的最大值", e);
            throw new BizException("上传文件的总的大小超出限制的最大值");
        }catch (Exception e) {
            logger.error("文件上传失败", e);
            throw new BizException("文件上传失败");
        }
        return result;
    }

    /**
     * 获得 上传文件 行列表
     * @param request
     * @return
     * @throws Exception
     */
    public static List<String> readUploadFileLines(HttpServletRequest request) throws BizException{
        //上传时生成的临时文件保存目录
        File uploadDir = new File(UPLOAD_PATH);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        List<String> lines = null;
        try{
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置工厂的缓冲区的大小，当上传的文件大小超过缓冲区的大小时，就会生成一个临时文件存放到指定的临时目录当中。
            factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB，如果不指定，那么缓冲区的大小默认是10KB
            factory.setRepository(uploadDir);
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            //监听文件上传进度
            upload.setProgressListener(new ProgressListener(){
                public void update(long pBytesRead, long pContentLength, int arg2) {
                    logger.info("上传的文件大小为：" + pContentLength + ",当前已处理：" + pBytesRead);
                }
            });
            //解决上传文件名的中文乱码
            upload.setHeaderEncoding("UTF-8");
            //3、判断提交上来的数据是否是上传表单的数据
            if(!ServletFileUpload.isMultipartContent(request)){
                return null;
            }
            upload.setFileSizeMax(UPLOAD_MAX);
            //设置上传文件总量的最大值
            upload.setSizeMax(UPLOAD_MAX*10);
            //4、使用ServletFileUpload解析器解析上传数据，解析结果返回的是一个List<FileItem>集合，每一个FileItem对应一个Form表单的输入项
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem item : list){
                //如果fileitem中封装的是普通输入项的数据
                if(!item.isFormField()){
                    //获取item中的上传文件的输入流
                    InputStream in = item.getInputStream();
                    lines = IOUtils.readLines(in, "UTF-8");
                    IOUtils.closeQuietly(in);
                    //删除处理文件上传时生成的临时文件
                    item.delete();
                    break;
                }
            }
        }catch (FileUploadBase.FileSizeLimitExceededException e) {
            logger.error("个文件超出最大值", e);
            throw new BizException("个文件超出最大值");
        }catch (FileUploadBase.SizeLimitExceededException e) {
            logger.error("上传文件的总的大小超出限制的最大值", e);
            throw new BizException("上传文件的总的大小超出限制的最大值");
        }catch (Exception e) {
            logger.error("文件上传失败", e);
            throw new BizException("文件上传失败");
        }
        return lines;
    }


    private static String makePath(String filename,String savePath){
        //得到文件名的hashCode的值，得到的就是filename这个字符串对象在内存中的地址
        int hashcode = filename.hashCode();
        int dir1 = hashcode&0xf;  //0--15
        int dir2 = (hashcode&0xf0)>>4;  //0-15
        //构造新的保存目录
        String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
        //File既可以代表文件也可以代表目录
        File file = new File(dir);
        //如果目录不存在
        if(!file.exists()){
            //创建目录
            file.mkdirs();
        }
        return dir;
    }


}
